# -*- coding:  utf-8 -*-
# accoufer : general library for cgi scripts
# 
# Copyright S.Keim 2011
# Available under the MIT licence see the COPYING file

import cgitb; cgitb.enable()  ## This line enables CGI error reporting

from config import *
import sys, os
import cgi, shelve

# access to accoufer generic library
sys.path.append(LIB_PATH)

SERVICE_URL = SERVICE_HOST + os.path.normpath(SERVICE_PATH)

# ###########################################################################
#                           Bdd
# ###########################################################################

# bdd paths
USER_PATH = DATA_PATH+"/user.db"  # users
EMAIL_PATH = DATA_PATH+"/email.db"  # email -> users index
CLIENT_PATH = DATA_PATH+"/clients.db"  # clients service
def offerpath(ref):
    return "%s/o%s.db"%(DATA_PATH, ref)




# bdd tuple value indexes
  # clients bdd values
CLT_REF   = 0
CLT_NAME  = 1
CLT_URL  = 2

  # users bdd values
USER_EMAIL  = 0
USER_PWD    = 1
USER_CREDITS = 2
USER_OFFERS = 3
  # USER OFFERS LIST
USER_OFF_CLIENT = 0
USER_OFF_REF = 1


  # offers bdd values
OFFER_REF = 0
OFFER_SVALUE = 1
OFFER_MIN_SVALUE = 2
OFFER_TX = 3
OFFER_LOGIN = 4

# bdd keys
COUNT_KEY = "count_"
NAME_KEY = "name_"
IP_KEY = "ip_"
URL_KEY = "url_"
X509_KEY ="x509_"

# ###########################################################################
#                           Html generation
# ###########################################################################

HTML_HEADER ="""Content-Type: text/html
%s

<html><head>
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <title>%s</title>
   <link rel="stylesheet" href="../site.css">
</head><body>
 <div id="logo"> <a href="./index.html"> <img src="../logo.png"/></a></div>
 <div id="login"> <a href="./xlogin">login/logout</a></div>
   %s
<SCRIPT language="Javascript" src="../main.js"></SCRIPT>
</body></html>"""
HTML = HTML_HEADER%("", "%s", '%s')

def fatal_error(txt):
    print HTML%("Fatal error", txt)
       

# ###########################################################################
#                           User login cookie
# ###########################################################################

#  md5 has moved in python >= 2.5 
import time, Cookie, sys
if sys.version_info[1]<5:
    import md5
    md5 = md5.new
else:
    import hashlib
    md5 = hashlib.md5

# set timeout to urlopen
import socket
socket.setdefaulttimeout(10)


# generate a secret PWD
st = os.stat(__file__) 
SECRET_PWD = "%s%s%s"%(st.st_ino, st.st_dev, st.st_mtime)
LIFETIME = 24*60*60 # 1 day

# crete cookie for user 
def cookieHeader(login):
   limit = str(int(time.time())+LIFETIME)
   ck = md5(login+limit+SECRET_PWD)
   header = 'Set-Cookie: user=%s.%s.%s; path=%s'%(login,limit,ck.hexdigest(), SERVICE_PATH)
   return header
def logoutHeader():   
   return 'Set-Cookie: user=; expires=Thu, 2 Aug 2001 00:00:00 UTC; path=%s' % SERVICE_PATH

# extract login,user from cookie 
def userCookie():
   try:     
       s = Cookie.SimpleCookie(os.environ.get("HTTP_COOKIE",""))["user"].value
   except KeyError:
       s = ""
   r = s.split('.')
   if len(r)==3:
       login,limit,chksum = r
       ck = md5(login+limit+SECRET_PWD)
       if int(limit)>time.time() and  ck.hexdigest()==chksum:
            users = shelve.open(USER_PATH)
            try:
                return login, users[login]
            except KeyError:
                pass
   print HTML_HEADER%(logoutHeader(), "Not logged", 
           """<h1>Not logged</h1><a href="./xlogin">Please login</a>""")
   sys.exit(0)

# ###########################################################################
#                           Read remote accoufer.txt
# ###########################################################################
import urllib
def urlopen(path, data=None):
    if SSL_PRIVATE_KEY is  None:
        opener = urllib.FancyURLopener()
    else:
        opener = urllib.FancyURLopener(key_file=SSL_PRIVATE_KEY, cert_file=SSL_PUBLIC_X509)
    response = opener.open(path, data)
    return response


def update_client(client, offers):
    data = {}
    try:
        f = urlopen(client+"/accoufer.txt")
    except IOError:
        return data
    for l in f:
        l = l.strip()
        if not l:
            continue
        i = l.index("=")
        key,value = l[:i],l[i+1:]
        data[key.strip()] = value.strip()
    
    url = data["url"]
    #x509 certificate
    if "ssl" in  data:
        certificate = urlopen(url+"/certificate.pem").read().strip()
        try:
            offers[X509_KEY]= certificate
        except IOError:
            return {}
            
    #other data    
    offers[URL_KEY] = url
    ip = data["ip"]
    offers[IP_KEY] = ip
    name = data.get("name-en", url)
    offers[NAME_KEY] = name
   
        
    return data
# ###########################################################################
#                           authenticate
# ###########################################################################        

import accerror, os
def authenticate(offers):    
    
    ip = os.environ["REMOTE_ADDR"]
    allowed_ip = offers.get(IP_KEY, "")
    
    allowed_x509 = offers.get(X509_KEY, "")
    x509 = os.environ.get("SSL_CLIENT_CERT", "").strip()
    
    if ip in allowed_ip and x509==allowed_x509:
        return 0, ""
    
        
    # changes in accoufer.txt file ?
    url = offers[URL_KEY]
    update_client(url, offers)     
    allowed = offers[IP_KEY]
    
    if ip not in allowed_ip:
        return accerror.ERR_IP_FORBIDEN, "ip not allowed: %s"%ip
    # todo : more robust check for certificat equality 
    if x509!=allowed_x509:
        return accerror.ERR_BAD_X509, "X.509 certificate mismatch " 

    return 0, ""

# ###########################################################################
#                           logs
# ###########################################################################        
import datetime
def logpath(login):
    return "%s/l_%s.log"%(DATA_PATH, login)
# actor : Y if log caused by user response    
def log(login, actor, remote, rvalue, value, solde):
    now = datetime.datetime.now().strftime("%Y/%m/%d %H:%M:%S")
    r = "%s\t%s\t%s\t%s\t%s\t%s\n" % (now, value, solde, actor, rvalue, remote)
    f = file(logpath(login), "a")
    f.write(r)

